// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_METRICS_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_METRICS_H_

#include <stddef.h>

#include "base/macros.h"
#include "base/time/time.h"
#include "content/browser/service_worker/service_worker_context_request_handler.h"
#include "content/browser/service_worker/service_worker_database.h"
#include "content/common/service_worker/service_worker_types.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponseError.h"
#include "ui/base/page_transition_types.h"

class GURL;

namespace content {

enum class EmbeddedWorkerStatus;

class ServiceWorkerMetrics {
public:
    // Used for UMA. Append-only.
    enum ReadResponseResult {
        READ_OK,
        READ_HEADERS_ERROR,
        READ_DATA_ERROR,
        NUM_READ_RESPONSE_RESULT_TYPES,
    };

    // Used for UMA. Append-only.
    enum WriteResponseResult {
        WRITE_OK,
        WRITE_HEADERS_ERROR,
        WRITE_DATA_ERROR,
        NUM_WRITE_RESPONSE_RESULT_TYPES,
    };

    // Used for UMA. Append-only.
    enum DeleteAndStartOverResult {
        DELETE_OK,
        DELETE_DATABASE_ERROR,
        DELETE_DISK_CACHE_ERROR,
        NUM_DELETE_AND_START_OVER_RESULT_TYPES,
    };

    // Used for UMA. Append-only.
    enum URLRequestJobResult {
        REQUEST_JOB_FALLBACK_RESPONSE,
        REQUEST_JOB_FALLBACK_FOR_CORS,
        REQUEST_JOB_HEADERS_ONLY_RESPONSE,
        REQUEST_JOB_STREAM_RESPONSE,
        REQUEST_JOB_BLOB_RESPONSE,
        REQUEST_JOB_ERROR_RESPONSE_STATUS_ZERO,
        REQUEST_JOB_ERROR_BAD_BLOB,
        REQUEST_JOB_ERROR_NO_PROVIDER_HOST,
        REQUEST_JOB_ERROR_NO_ACTIVE_VERSION,
        REQUEST_JOB_ERROR_NO_REQUEST,
        REQUEST_JOB_ERROR_FETCH_EVENT_DISPATCH,
        REQUEST_JOB_ERROR_BLOB_READ,
        REQUEST_JOB_ERROR_STREAM_ABORTED,
        REQUEST_JOB_ERROR_KILLED,
        REQUEST_JOB_ERROR_KILLED_WITH_BLOB,
        REQUEST_JOB_ERROR_KILLED_WITH_STREAM,
        REQUEST_JOB_ERROR_DESTROYED,
        REQUEST_JOB_ERROR_DESTROYED_WITH_BLOB,
        REQUEST_JOB_ERROR_DESTROYED_WITH_STREAM,
        REQUEST_JOB_ERROR_BAD_DELEGATE,
        REQUEST_JOB_ERROR_REQUEST_BODY_BLOB_FAILED,
        NUM_REQUEST_JOB_RESULT_TYPES,
    };

    // Used for UMA. Append-only.
    enum class StopStatus {
        NORMAL,
        DETACH_BY_REGISTRY,
        TIMEOUT,
        // Add new types here.
        NUM_TYPES
    };

    // Used for UMA. Append-only.
    // This class is used to indicate which event is fired/finished. Most events
    // have only one request that starts the event and one response that finishes
    // the event, but the fetch and the foreign fetch event have two responses, so
    // there are two types of EventType to break down the measurement into two:
    // FETCH/FOREIGN_FETCH and FETCH_WAITUNTIL/FOREIGN_FETCH_WAITUNTIL.
    // Moreover, FETCH is separated into the four: MAIN_FRAME, SUB_FRAME,
    // SHARED_WORKER and SUB_RESOURCE for more detailed UMA.
    enum class EventType {
        ACTIVATE = 0,
        INSTALL = 1,
        // FETCH = 2,  // Obsolete
        SYNC = 3,
        NOTIFICATION_CLICK = 4,
        PUSH = 5,
        // GEOFENCING = 6,  // Obsolete
        // SERVICE_PORT_CONNECT = 7,  // Obsolete
        MESSAGE = 8,
        NOTIFICATION_CLOSE = 9,
        FETCH_MAIN_FRAME = 10,
        FETCH_SUB_FRAME = 11,
        FETCH_SHARED_WORKER = 12,
        FETCH_SUB_RESOURCE = 13,
        UNKNOWN = 14, // Used when event type is not known.
        FOREIGN_FETCH = 15,
        FETCH_WAITUNTIL = 16,
        FOREIGN_FETCH_WAITUNTIL = 17,
        NAVIGATION_HINT_LINK_MOUSE_DOWN = 18,
        NAVIGATION_HINT_LINK_TAP_UNCONFIRMED = 19,
        NAVIGATION_HINT_LINK_TAP_DOWN = 20,
        // Used when external consumers want to add a request to
        // ServiceWorkerVersion to keep it alive.
        EXTERNAL_REQUEST = 21,
        PAYMENT_REQUEST = 22,
        // Add new events to record here.
        NUM_TYPES
    };

    // Used for UMA. Append only.
    enum class Site {
        OTHER, // Obsolete for UMA. Use WITH_FETCH_HANDLER or
        // WITHOUT_FETCH_HANDLER.
        NEW_TAB_PAGE,
        WITH_FETCH_HANDLER,
        WITHOUT_FETCH_HANDLER,
        PLUS,
        INBOX,
        DOCS,
        NUM_TYPES
    };

    // Not used for UMA.
    enum class StartSituation {
        UNKNOWN,
        DURING_STARTUP,
        EXISTING_PROCESS,
        NEW_PROCESS
    };

    // Used for UMA. Append only.
    // This enum describes how an activated worker was found and prepared (i.e.,
    // reached the RUNNING status) in order to dispatch a fetch event to.
    enum class WorkerPreparationType {
        UNKNOWN,
        // The worker was already starting up. We waited for it to finish.
        STARTING,
        // The worker was already running.
        RUNNING,
        // The worker was stopping. We waited for it to stop, and then started it
        // up.
        STOPPING,
        // The worker was in the stopped state. We started it up, and startup
        // required a new process to be created.
        START_IN_NEW_PROCESS,
        // The worker was in the stopped state. We started it up, and it used an
        // existing process.
        START_IN_EXISTING_PROCESS,
        // The worker was in the stopped state. We started it up, and this occurred
        // during browser startup.
        START_DURING_STARTUP,
        // Add new types here.
        NUM_TYPES
    };

    // Not used for UMA.
    enum class LoadSource { NETWORK,
        HTTP_CACHE,
        SERVICE_WORKER_STORAGE };

    // Converts an event type to a string. Used for tracing.
    static const char* EventTypeToString(EventType event_type);

    // If the |url| is not a special site, returns Site::OTHER.
    static Site SiteFromURL(const GURL& url);

    // Returns true when the event is for a navigation hint.
    static bool IsNavigationHintEvent(EventType event_type);

    // Excludes NTP scope from UMA for now as it tends to dominate the stats and
    // makes the results largely skewed. Some metrics don't follow this policy
    // and hence don't call this function.
    static bool ShouldExcludeSiteFromHistogram(Site site);

    // Used for ServiceWorkerDiskCache.
    static void CountInitDiskCacheResult(bool result);
    static void CountReadResponseResult(ReadResponseResult result);
    static void CountWriteResponseResult(WriteResponseResult result);

    // Used for ServiceWorkerDatabase.
    static void CountOpenDatabaseResult(ServiceWorkerDatabase::Status status);
    static void CountReadDatabaseResult(ServiceWorkerDatabase::Status status);
    static void CountWriteDatabaseResult(ServiceWorkerDatabase::Status status);
    static void RecordDestroyDatabaseResult(ServiceWorkerDatabase::Status status);

    // Used for ServiceWorkerStorage.
    static void RecordPurgeResourceResult(int net_error);
    static void RecordDeleteAndStartOverResult(DeleteAndStartOverResult result);

    // Counts the number of page loads controlled by a Service Worker.
    static void CountControlledPageLoad(Site site,
        const GURL& url,
        bool is_main_frame_load,
        ui::PageTransition page_transition,
        size_t redirect_chain_length);

    // Records the result of trying to start a worker. |is_installed| indicates
    // whether the version has been installed.
    static void RecordStartWorkerStatus(ServiceWorkerStatusCode status,
        EventType purpose,
        bool is_installed);

    // Records the time taken to successfully start a worker. |is_installed|
    // indicates whether the version has been installed.
    static void RecordStartWorkerTime(base::TimeDelta time,
        bool is_installed,
        StartSituation start_situation,
        EventType purpose);

    // Records metrics for the preparation of an activated Service Worker for a
    // main frame navigation.
    CONTENT_EXPORT static void RecordActivatedWorkerPreparationForMainFrame(
        base::TimeDelta time,
        EmbeddedWorkerStatus initial_worker_status,
        StartSituation start_situation,
        bool did_navigation_preload);

    // Records the result of trying to stop a worker.
    static void RecordWorkerStopped(StopStatus status);

    // Records the time taken to successfully stop a worker.
    static void RecordStopWorkerTime(base::TimeDelta time);

    static void RecordActivateEventStatus(ServiceWorkerStatusCode status,
        bool is_shutdown);
    static void RecordInstallEventStatus(ServiceWorkerStatusCode status);

    static void RecordForeignFetchRegistrationCount(size_t scope_count,
        size_t origin_count);

    // Records how much of dispatched events are handled while a Service
    // Worker is awake (i.e. after it is woken up until it gets stopped).
    static void RecordEventHandledRatio(EventType event,
        size_t handled_events,
        size_t fired_events);

    // Records the precision of the speculative launch of Service Workers for
    // each navigation hint type when the worker is stopped. If there was no
    // main/sub frame fetch event fired on the worker, |frame_fetch_event_fired|
    // is false. This means that the speculative launch wasn't helpful.
    static void RecordNavigationHintPrecision(EventType start_worker_purpose,
        bool frame_fetch_event_fired);

    // Records how often a dispatched event times out.
    static void RecordEventTimeout(EventType event);

    // Records the amount of time spent handling an event.
    static void RecordEventDuration(EventType event,
        base::TimeDelta time,
        bool was_handled);

    // Records the time taken between sending an event IPC from the browser
    // process to a Service Worker and executing the event handler in the Service
    // Worker.
    static void RecordEventDispatchingDelay(EventType event,
        base::TimeDelta time,
        Site site_for_metrics);

    // Records the result of dispatching a fetch event to a service worker.
    static void RecordFetchEventStatus(bool is_main_resource,
        ServiceWorkerStatusCode status);

    // Records result of a ServiceWorkerURLRequestJob that was forwarded to
    // the service worker.
    static void RecordURLRequestJobResult(bool is_main_resource,
        URLRequestJobResult result);

    // Records the error code provided when the renderer returns a response with
    // status zero to a fetch request.
    static void RecordStatusZeroResponseError(
        bool is_main_resource,
        blink::WebServiceWorkerResponseError error);

    // Records the mode of request that was fallbacked to the network.
    static void RecordFallbackedRequestMode(FetchRequestMode mode);

    // Called at the beginning of each ServiceWorkerVersion::Dispatch*Event
    // function. Records the time elapsed since idle (generally the time since the
    // previous event ended).
    static void RecordTimeBetweenEvents(base::TimeDelta time);

    // The following record steps of EmbeddedWorkerInstance's start sequence.
    static void RecordProcessCreated(bool is_new_process);
    static void RecordTimeToSendStartWorker(base::TimeDelta duration,
        StartSituation start_situation);
    static void RecordTimeToURLJob(base::TimeDelta duration,
        StartSituation start_situation);
    static void RecordTimeToLoad(base::TimeDelta duration,
        LoadSource source,
        StartSituation start_situation);
    static void RecordTimeToStartThread(base::TimeDelta duration,
        StartSituation start_situation);
    static void RecordTimeToEvaluateScript(base::TimeDelta duration,
        StartSituation start_situation);

    static const char* LoadSourceToString(LoadSource source);
    static StartSituation GetStartSituation(bool is_browser_startup_complete,
        bool is_new_process);

    // Records the result of a start attempt that occurred after the worker had
    // failed |failure_count| consecutive times.
    static void RecordStartStatusAfterFailure(int failure_count,
        ServiceWorkerStatusCode status);

    // Records the size of Service-Worker-Navigation-Preload header when the
    // navigation preload request is to be sent.
    static void RecordNavigationPreloadRequestHeaderSize(size_t size);

    // Records timings for the navigation preload response and how
    // it compares to starting the worker.
    // |worker_start| is the time it took to prepare an activated and running
    // worker to receive the fetch event. |initial_worker_status| and
    // |start_situation| describe the preparation needed.
    // |response_start| is the time it took until the navigation preload response
    // started.
    CONTENT_EXPORT static void RecordNavigationPreloadResponse(
        base::TimeDelta worker_start,
        base::TimeDelta response_start,
        EmbeddedWorkerStatus initial_worker_status,
        StartSituation start_situation);

    // Records the result of trying to handle a request for a service worker
    // script.
    static void RecordContextRequestHandlerStatus(
        ServiceWorkerContextRequestHandler::CreateJobStatus status,
        bool is_installed,
        bool is_main_script);

private:
    DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceWorkerMetrics);
};

} // namespace content

#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_METRICS_H_
